#include <iostream>
#include <iomanip>
#include <GL/glut.h>    // Header File For The GLUT Library 
#include <GL/gl.h>	// Header File For The OpenGL32 Library
#include <GL/glu.h>	// Header File For The GLu32 Library
#include <unistd.h>     // Header file for sleeping.
#include <math.h>
#include <sys/time.h>
#include <ImageMagick/Magick++.h>

using namespace std;

/* ascii code for the escape key */
#define ESCAPE 27

#define DEBUG

#ifdef DEBUG
#define PRINT_LOG() cout << left << setw(15) << "函数名" << ":"  \
	<< left << setw(15) << __func__ \
	<< left << setw(10) << "行号: " \
	<< setw(5) << __LINE__ << endl;
#else
#define PRINT_LOG()
#endif

#define IMAGE "../media/textures/Pensive Parakeet.jpg"

const int fps = 60;
const float tpf = 1.0 / 60;

int			window;
GLuint      texture[1];
GLenum		renderMode = GL_RENDERER;
int			cursorX;
int			cursorY;
int			curCursorX;
int			curCursorY;
#define		BUFSIZE 512
GLuint		selectBuf[BUFSIZE];

void LoadGLTextures()
{
	Magick::Image *pImage = NULL;
	Magick::Blob  blob;

	try {
		pImage = new Magick::Image(IMAGE);
		pImage->write(&blob, "RGB");
	} catch (Magick::Error& error) {
		std::cerr << "加载纹理失败:" << error.what() << std::endl;
		exit(-1);
	}

    glGenTextures(1, &texture[0]);
    glBindTexture(GL_TEXTURE_2D, texture[0]);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);


    glTexImage2D(GL_TEXTURE_2D, 0, 3, pImage->columns(), pImage->rows(), 0, GL_RGB, GL_UNSIGNED_BYTE, blob.data());
	delete pImage;
}

/* A general OpenGL initialization function.  Sets all of the initial parameters. */
void InitGL(int Width, int Height)	        // We call this right after our OpenGL window is created.
{
    LoadGLTextures();
    glEnable(GL_TEXTURE_2D);
    glClearColor(0.0f, 0.0f, 0.0f, 0.0f);		// This Will Clear The Background Color To Black
    glClearDepth(1.0);				// Enables Clearing Of The Depth Buffer
    glDepthFunc(GL_LESS);				// The Type Of Depth Test To Do
    glEnable(GL_DEPTH_TEST);			// Enables Depth Testing
    glShadeModel(GL_SMOOTH);			// Enables Smooth Color Shading

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();				// Reset The Projection Matrix

    gluPerspective(45.0f,(GLfloat)Width/(GLfloat)Height,0.1f,100.0f);	// Calculate The Aspect Ratio Of The Window

    glMatrixMode(GL_MODELVIEW);

    //glPolygonMode(GL_BACK, GL_FILL);
    //glPolygonMode(GL_FRONT, GL_LINE);
}

/* The function called when our window is resized (which shouldn't happen, because we're fullscreen) */
void ReSizeGLScene(int Width, int Height)
{
    if (Height==0)				// Prevent A Divide By Zero If The Window Is Too Small
        Height=1;

    glViewport(0, 0, Width, Height);		// Reset The Current Viewport And Perspective Transformation

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();

    gluPerspective(45.0f,(GLfloat)Width/(GLfloat)Height,0.1f,100.0f);
    glMatrixMode(GL_MODELVIEW);
}

struct timeval old, cur;

static void drawCursor() {
    GLint viewport[4];

	glMatrixMode(GL_PROJECTION);
	glPushMatrix();
	glLoadIdentity();

    glGetIntegerv(GL_VIEWPORT, viewport);
	glOrtho(viewport[0], viewport[0] + viewport[2],
			viewport[1], viewport[1] + viewport[3],
			1, -1);
	
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();

	glPushAttrib(GL_CURRENT_BIT);
	glColor3f(1, 0, 0);
	glPointSize(10.0f);
	glBegin(GL_POINTS);
	glVertex3f(cursorX, viewport[3] - cursorY, 0);
	glEnd();
	glPopAttrib();


	glMatrixMode(GL_PROJECTION);
	glPopMatrix();
	glMatrixMode(GL_MODELVIEW);
	
}

static void normalModeDraw() {
    glBindTexture(GL_TEXTURE_2D, texture[0]);

	glTranslatef(0, 0, -5);
    glBegin(GL_QUADS);
    glVertex3f(-1, -1, 0); glTexCoord2f(0, 0);
    glVertex3f( 1, -1, 0); glTexCoord2f(1, 0);
    glVertex3f( 1,  1, 0); glTexCoord2f(1, 1);
    glVertex3f(-1,  1, 0); glTexCoord2f(0, 1);
    glEnd();
}

static void selectModeDraw() {
	glPushName(1);

    glBindTexture(GL_TEXTURE_2D, texture[0]);

	glTranslatef(0, 0, -5);
    glBegin(GL_QUADS);
    glVertex3f(-1, -1, 0); glTexCoord2f(0, 0);
    glVertex3f( 1, -1, 0); glTexCoord2f(1, 0);
    glVertex3f( 1,  1, 0); glTexCoord2f(1, 1);
    glVertex3f(-1,  1, 0); glTexCoord2f(0, 1);
    glEnd();

	glPopName();
}


static void startPicking() {
	PRINT_LOG();
    GLint viewport[4];

    glSelectBuffer(BUFSIZE, selectBuf);
    glRenderMode(GL_SELECT);

    glMatrixMode(GL_PROJECTION);
    glPushMatrix();
    glLoadIdentity();

    glGetIntegerv(GL_VIEWPORT, viewport);
    gluPickMatrix(cursorX, viewport[3] - cursorY, 5, 5, viewport);
    gluPerspective(45, 1, 0.1, 1000);

    glMatrixMode(GL_MODELVIEW);
    glInitNames();
}

static void processHits(GLint hits, GLuint buffer[]) {
	PRINT_LOG();
	GLuint names, *pbuf, minZ;

	pbuf = (GLuint *)buffer;
	minZ = 0xffffffff;

	for (int i = 0; i < hits; ++i) {
		names = *pbuf++;
		std::cout << "选择对象的编号为:" << names << endl;
	}
}

static void stopPicking() {
	PRINT_LOG();
	glMatrixMode(GL_PROJECTION);
	glPopMatrix();
	glMatrixMode(GL_MODELVIEW);
	glFlush();

	int hitsRC = glRenderMode(GL_RENDER);
	std::cout << "命名个数: hitsRC:" << hitsRC << std::endl;
	if (hitsRC != 0) {
		processHits(hitsRC, selectBuf);
	}
	renderMode = GL_RENDER;

}

/* The main drawing function. */
void DrawGLScene()
{

    gettimeofday(&cur, 0);
    
    
    int x, y;
    float float_x, float_y, float_xb, float_yb;
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);		// Clear The Screen And The Depth Buffer

	if (renderMode == GL_SELECT) {
		startPicking();
	}
	glLoadIdentity();				// Reset The View


	if (renderMode == GL_SELECT) { 
		selectModeDraw();
	} else {
		normalModeDraw();
		drawCursor();	
	}


	if (renderMode == GL_SELECT) {
		stopPicking();
	} else {
		// since this is double buffered, swap the buffers to display what just got drawn.
		glutSwapBuffers();
	}
}

static void toggleFullscreen(bool fullscreen) {
    /* Go fullscreen.  This is as soon as possible. */
    if (fullscreen)
        glutFullScreen();
    else {
    }
}

/* The function called whenever a key is pressed. */
void keyPressed(unsigned char key, int x, int y) 
{
    /* avoid thrashing this procedure */
    usleep(100);

    switch(key) {
        /* If escape is pressed, kill everything. */
        case ESCAPE:
            /* exit the program...normal termination. */
            glutDestroyWindow(window); 
            exit(0);

        default:
            break;
    }
}

void mouseFunc(int button, int state, int x, int y) {
    PRINT_LOG();
    usleep(100);

	if (button  == GLUT_LEFT_BUTTON) {
		renderMode = GL_SELECT;
		cursorX = x;
		cursorY = y;
	}
}

void motionFunc(int x, int y) {
    PRINT_LOG();
}

void passiveMoveFunc(int x, int y) {
    //PRINT_LOG();
	curCursorX = x;
	curCursorY = y;
}

int main(int argc, char **argv) 
{  
    /* Initialize GLUT state - glut will take any command line arguments that pertain to it or 
       X Windows - look at its documentation at http://reality.sgi.com/mjk/spec3/spec3.html */  
    glutInit(&argc, argv);  

    /* Select type of Display mode:   
       Double buffer 
       RGBA color
       Alpha components supported 
       Depth buffer */  
    glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_ALPHA | GLUT_DEPTH);  

    /* get a 640 x 480 window */
    glutInitWindowSize(640, 480);  

    /* the window starts at the upper left corner of the screen */
    glutInitWindowPosition(0, 0);  

    /* Open a window */  
    window = glutCreateWindow("Jeff Molofee's GL Code Tutorial ... NeHe '99");  

    /* Register the function to do all our OpenGL drawing. */
    glutDisplayFunc(&DrawGLScene);  


    /* Even if there are no events, redraw our gl scene. */
    glutIdleFunc(&DrawGLScene);

    /* Register the function called when our window is resized. */
    glutReshapeFunc(&ReSizeGLScene);

    /* Register the function called when the keyboard is pressed. */
    glutKeyboardFunc(&keyPressed);

    /* Register the function called when the mouse is clicked. */
    glutMouseFunc(mouseFunc);

    /* Register the function called when the mouse button is pressed. */
    glutMotionFunc(motionFunc);

    /* Register the function called when the mouse is move without button pressed. */
    glutPassiveMotionFunc(passiveMoveFunc);

    /* Initialize our window. */
    InitGL(640, 480);

    /* Start Event Processing Engine */  
    glutMainLoop();  

    return 1;
}

